package socket

import (
	"encoding/json"
	"net/http"
	"github.com/gorilla/websocket"
	"github.com/satori"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego"
)

type ClientManager struct {
	clients    map[*Client]bool
	broadcast  chan []byte
	send       chan []byte
	ring       chan *Message
	access     chan *Client
	register   chan *Client
	unregister chan *Client
}

type Client struct {
	id       string
	shopId   string
	shopName string
	socket   *websocket.Conn
	send     chan []byte
}

type Result  struct {
	Msg  string `json:"msg,omitempty"`
	Succ bool `json:"succ,omitempty"`
	Data Message `json:"data,omitempty"`
}

type Message struct {
	Table       string `json:"table,omitempty"`
	ShopId      string `json:"shopId,omitempty"`
	ShopName    string `json:"shopName,omitempty"`
	Content     string `json:"content,omitempty"`
	MessageType int8 `json:"messageType"`
	RingType    int8 `json:"ringType"`
	RingContent string  `json:"ringContent,omitempty"`
	RingUrl     string  `json:"url,omitempty"`
}

var manager = ClientManager{
	ring:  make(chan *Message),
	broadcast:  make(chan []byte),
	send:  make(chan []byte),
	access :    make(chan *Client),
	register:   make(chan *Client),
	unregister: make(chan *Client),
	clients:    make(map[*Client]bool),
}

func (manager *ClientManager) start() {
	for {
		select {



		case conn := <-manager.register:

			manager.clients[conn] = true
			jsonMessage, _ := json.Marshal(&Result{Msg:"服务注册成功", Data:Message{MessageType:0, ShopId:conn.shopId, ShopName:conn.shopName}, Succ:true})

			conn.send <- jsonMessage

		case conn := <-manager.unregister:

			if _, ok := manager.clients[conn]; ok {

				jsonMessage, _ := json.Marshal(&Result{Msg:"服务注銷成功", Data:Message{MessageType:1, ShopId:conn.shopId, ShopName:conn.shopName}, Succ:true})
				conn.send <- jsonMessage

				close(conn.send)
				delete(manager.clients, conn)

			}



		case message := <-manager.broadcast:


			for conn := range manager.clients {

				select {
				case conn.send <- message:
				default:
					close(conn.send)
					delete(manager.clients, conn)
				}

			}

		case message := <-manager.ring:


			for conn := range manager.clients {

				if (conn.shopId == message.ShopId  &&manager.clients[conn] ) {

					message.ShopName = conn.shopName

					result := Result{Msg:"", Data:*message, Succ:true}

					jsonResult, _ := json.Marshal(result)

					select {
					case conn.send <- jsonResult:
					default:
						close(conn.send)
						delete(manager.clients, conn)
					}
				}
			}
		}
	}
}

func RingPush(shopId, table, ringContent, url string, ringType int8) *Message {

	//jsonMessage, _ := json.Marshal(&Message{Sender: sender, Recipient :recipient, Content: string(message)})

	msg := Message{MessageType:2, Content: "呼叫服务", ShopId:shopId, Table:table, RingType:ringType, RingContent:ringContent, RingUrl:url}

	manager.ring <- &msg

	return &msg
}

func (c *Client) read() {

	defer func() {
		manager.unregister <- c
		c.socket.Close()
	}()

	for {
		_, message, err := c.socket.ReadMessage()

		if err != nil {
			manager.unregister <- c
			c.socket.Close()
			break
		}

		var register Message

		if err := json.Unmarshal(message, &register); err == nil {

			//注册
			if register.MessageType == 0 {

				c.shopId = register.ShopId
				c.shopName = register.ShopName

				manager.register <- c
			}

		} else {

			result := new(Result)
			result.Msg = err.Error()
			result.Succ = false
			resultStr, _ := json.Marshal(result)

			c.send <- resultStr
		}

	}
}

func (c *Client) write() {

	defer func() {
		c.socket.Close()
	}()

	for {
		select {
		case message, ok := <-c.send:

			if !ok {
				c.socket.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}

			c.socket.WriteMessage(websocket.TextMessage, message)
		}
	}
}

func OpenService() {
	go manager.start()
	http.HandleFunc("/", wsPage)

	go http.ListenAndServe(":" + beego.AppConfig.String("socketport"), nil)

	logs.Debug("socket server Running on http://:" + beego.AppConfig.String("socketport"))
}

func wsPage(res http.ResponseWriter, req *http.Request) {

	conn, error := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool {
		return true
	}}).Upgrade(res, req, nil)

	if error != nil {
		http.NotFound(res, req)
		return
	}

	logs.Debug(conn.RemoteAddr().String())

	id, _ := uuid.NewV4()

	client := &Client{id: id.String(), socket: conn, send: make(chan []byte)}

	go client.read()

	go client.write()

}
